﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Security;
using System.Windows;
using System.Windows.Controls;
using Microsoft.Xrm.Sdk.Client;
using Microsoft.Xrm.Sdk.Discovery;
using Microsoft.Xrm.Client;
using Microsoft.Xrm.Client.Services;
using System.Security.Cryptography.X509Certificates;
using McTools.Xrm.Connection;

//This code uses McTools.Xrm.Connection by codeplex and been extended/modified to be used as WPF service connector

namespace BAC.CRMService.UI.Engine
{
    public partial class NewConnectionWindow : Window
    {
        #region Variables

        /// <summary>
        /// Détail de connexion courant
        /// </summary>
        ConnectionDetail detail;

        /// <summary>
        /// List of Crm server organizations
        /// </summary>
        List<OrganizationDetail> organizations;

        /// <summary>
        /// Indique si l'utilisateur a demandé à se connecter
        /// au serveur
        /// </summary>
        bool doConnect;

        public CrmConnections ConnectionList { get; set; }

        #endregion

        #region Properties

        /// <summary>
        /// Obtient la valeur qui définit si l'utilisateur a demandé 
        /// à se connecter au serveur
        /// </summary>
        public bool DoConnect
        {
            get { return doConnect; }
        }

        /// <summary>
        /// Obtient ou définit le détail de la connexion courante
        /// </summary>
        public ConnectionDetail CrmConnectionDetail
        {
            get { return detail; }
            set { detail = value; }
        }

        readonly bool isCreationMode;

        #endregion

        #region Constructors

        public NewConnectionWindow(bool isCreation)
        {
            InitializeComponent();
            isCreationMode = isCreation;
            cbbOnlineEnv.SelectedIndex = 0;

            rbIntegrated.IsChecked = true;
            tbServerName.ToolTip = "For CRM Online or OSDP, use:\r\ncrm.dynamics.com for North America\r\ncrm4.dynamics.com for EMEA\r\ncrm5.dynamics.com for Asia Pacific\r\n\r\nFor OnPremise:\r\nUse the server name\r\n\r\nFor IFD:\r\nUse <organization_name>.<domain>.<extension>";
            tbServerPort.ToolTip = "Specify port only if different from 80 or 443 (SSL)";
            tbHomeRealmUri.ToolTip = "(Optional) In specific case, you should need to specify the home realm url to authenticate through ADFS";
            // allows for validation of SSL conversations
            ServicePointManager.ServerCertificateValidationCallback += ValidateRemoteCertificate;
        }
        // callback used to validate the certificate in an SSL conversation
        private static bool ValidateRemoteCertificate(
        object sender,
            X509Certificate certificate,
            X509Chain chain,
            SslPolicyErrors policyErrors
        )
        {
            return true;
        }
        #endregion Constructors

        #region Handlers

        #region Settings behavior

        private void ChkUseIfdChecked(object sender, RoutedEventArgs e)
        {
            cbUseSsl.IsChecked = true;
            cbUseOnline.IsEnabled = false;
            cbUserOsdp.IsEnabled = false;

            rbCustom.IsChecked = true;
            rbCustom.IsEnabled = false;
            rbIntegrated.IsEnabled = false;
        }

        private void ChkUseIfdUnchecked(object sender, RoutedEventArgs e)
        {
            cbUseSsl.IsChecked = false;
            cbUseOnline.IsEnabled = true;
            cbUserOsdp.IsEnabled = true;

            rbCustom.IsEnabled = true;
            rbIntegrated.IsEnabled = true;
        }

        private void ChkUseOnlineChecked(object sender, RoutedEventArgs e)
        {
            cbUseIfd.IsEnabled = false;
            cbUseSsl.IsEnabled = false;
            cbUseSsl.IsChecked = true;
            tbServerName.Visibility = Visibility.Hidden;
            cbbOnlineEnv.Visibility = Visibility.Visible;
            rbCustom.IsChecked = true;
            rbCustom.IsEnabled = false;
            rbIntegrated.IsEnabled = false;
            tbDomain.IsEnabled = false;
            string selectedText = cbbOnlineEnv.SelectedValue.ToString();
            tbServerName.Text = selectedText;
            tbServerPort.IsEnabled = false;
            tbHomeRealmUri.IsEnabled = false;
        }

        private void ChkUseOnlineUnchecked(object sender, RoutedEventArgs e)
        {
            cbUseIfd.IsEnabled = true;
            cbUseSsl.IsEnabled = true;
            cbUseSsl.IsChecked = false;

            rbCustom.IsEnabled = true;
            rbIntegrated.IsEnabled = true;
            tbServerName.Text = string.Empty;
            tbDomain.IsEnabled = true;
            tbServerName.Visibility = Visibility.Visible;
            cbbOnlineEnv.Visibility = Visibility.Hidden;
            tbServerPort.IsEnabled = true;
            tbHomeRealmUri.IsEnabled = true;
        }

        private void ChkUserOsdpChecked(object sender, RoutedEventArgs e)
        {
            cbUseOnline.IsChecked = true;
            cbUseOnline.IsEnabled = false;
        }

        private void ChkUserOsdpUnchecked(object sender, RoutedEventArgs e)
        {
            cbUseOnline.IsEnabled = true;
        }

        private void RdbIntegratedChecked(object sender, RoutedEventArgs e)
        {
            tbDomain.IsEnabled = false;
            tbLogin.IsEnabled = false;
            tbPassword.IsEnabled = false;
            cbSavePassword.IsEnabled = false;
        }

        private void RdbCustomChecked(object sender, RoutedEventArgs e)
        {
            tbDomain.IsEnabled = true;
            tbLogin.IsEnabled = true;
            tbPassword.IsEnabled = true;
            cbSavePassword.IsEnabled = true;
        }

        #endregion Settings behavior

        private void WindowLoaded1(object sender, RoutedEventArgs e)
        {
            tbServerName.Visibility = Visibility.Visible;
            cbbOnlineEnv.Visibility = Visibility.Hidden;
            if (detail != null)
            {
                FillValues();
            }
        }

        private void BtnGetOrgsClick(object sender, RoutedEventArgs e)
        {
            GetOrganizations();
        }

        private void BtnValidateClick(object sender, RoutedEventArgs e)
        {
            Validate();
            //DialogResult = true;
            Close();
        }

        private void BtnCancelClick(object sender, RoutedEventArgs e)
        {
            DialogResult = false;
            Close();
        }


        private void cbbOnlineEnv_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (cbUseOnline.IsChecked.Value)
            {
                string selectedText = cbbOnlineEnv.SelectedValue.ToString();
                tbServerName.Text = selectedText;
            }
            else
            {
                tbServerName.Text = string.Empty;
            }
        }

        #endregion Handlers

        #region Methods

        private void GetOrganizations()
        {
            string warningMessage = string.Empty;
            bool goodAuthenticationData = false;
            bool goodServerData = false;

            // Check data filled by user
            if (rbIntegrated.IsChecked != null && rbIntegrated.IsChecked.Value ||
                (
                rbCustom.IsChecked != null && rbCustom.IsChecked.Value &&
                (tbDomain.Text.Length > 0
                || cbUseIfd.IsChecked != null && cbUseIfd.IsChecked.Value
                || cbUseOnline.IsChecked != null && cbUseOnline.IsChecked.Value) &&
                tbLogin.Text.Length > 0 &&
                tbPassword.Password.Length > 0
                ))
                goodAuthenticationData = true;

            if (tbServerName.GetValue(TextBox.TextProperty).ToString().Length > 0)
                goodServerData = true;

            if (!goodServerData)
            {
                warningMessage += "Please provide server name\r\n";
            }
            if (!goodAuthenticationData)
            {
                warningMessage += "Please fill all authentication textboxes\r\n";
            }

            if (warningMessage.Length > 0)
            {
                MessageBox.Show(this, warningMessage, "Warning", MessageBoxButton.OK, MessageBoxImage.Warning);
            }
            else
            {
                // Save connection details in structure
                if (isCreationMode || detail == null)
                {
                    detail = new ConnectionDetail();
                }

                detail.ConnectionName = txtConnectionName.Text;
                detail.IsCustomAuth = rbCustom.IsChecked != null && rbCustom.IsChecked.Value;
                detail.UseSsl = cbUseSsl.IsChecked != null && cbUseSsl.IsChecked.Value;
                detail.UseOnline = cbUseOnline.IsChecked != null && cbUseOnline.IsChecked.Value;
                detail.UseOsdp = cbUserOsdp.IsChecked != null && cbUserOsdp.IsChecked.Value;
                detail.ServerName = tbServerName.Text;
                detail.ServerPort = tbServerPort.Text;
                detail.UserDomain = tbDomain.Text;
                detail.UserName = tbLogin.Text;
                detail.UserPassword = tbPassword.Password;
                detail.SavePassword = cbSavePassword.IsChecked != null && cbSavePassword.IsChecked.Value;
                detail.UseIfd = cbUseIfd.IsChecked != null && cbUseIfd.IsChecked.Value;
                detail.HomeRealmUrl = (tbHomeRealmUri.Text.Length > 0 ? tbHomeRealmUri.Text : null);

                detail.AuthType = AuthenticationProviderType.ActiveDirectory;

                if (cbUseIfd.IsChecked.Value)
                {
                    detail.AuthType = AuthenticationProviderType.Federation;
                }
                else if (cbUserOsdp.IsChecked.Value)
                {
                    detail.AuthType = AuthenticationProviderType.OnlineFederation;
                }
                else if (cbUseOnline.IsChecked.Value)
                {
                    detail.AuthType = AuthenticationProviderType.LiveId;
                }

                // Launch organization retrieval
                comboBoxOrganizations.Items.Clear();
                //Thread retrieveOrgThread = new Thread(new ThreadStart(FillOrganizations));
                //retrieveOrgThread.Start();
                FillOrganizations();
            }
        }

        private void Validate()
        {
            if (txtConnectionName.Text.Length == 0)
            {
                MessageBox.Show(this, "You must define a name for this connection!", "Warning", MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            if (comboBoxOrganizations.SelectedItem == null
                && !(cbUseIfd.IsChecked != null && cbUseIfd.IsChecked.Value
                || cbUserOsdp.IsChecked != null && cbUserOsdp.IsChecked.Value))
            {
                MessageBox.Show(this, "You must select an organization!", "Warning", MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            if (tbPassword.Password.Length == 0 && rbCustom.IsChecked != null && rbCustom.IsChecked.Value)
            {
                MessageBox.Show(this, "You must define a password!", "Warning", MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            if (detail == null)
                detail = new ConnectionDetail();

            // Save connection details in structure
            detail.ConnectionName = txtConnectionName.Text;
            detail.IsCustomAuth = rbCustom.IsChecked != null && rbCustom.IsChecked.Value;
            detail.UseSsl = cbUseSsl.IsChecked != null && cbUseSsl.IsChecked.Value;
            detail.UseOnline = cbUseOnline.IsChecked != null && cbUseOnline.IsChecked.Value;
            detail.UseOsdp = cbUserOsdp.IsChecked != null && cbUserOsdp.IsChecked.Value;
            detail.ServerName = tbServerName.Text;
            detail.ServerPort = tbServerPort.Text;
            detail.UserDomain = tbDomain.Text;
            detail.UserName = tbLogin.Text;
            detail.UserPassword = tbPassword.Password;
            detail.SavePassword = cbSavePassword.IsChecked != null && cbSavePassword.IsChecked.Value;
            detail.UseIfd = cbUseIfd.IsChecked != null && cbUseIfd.IsChecked.Value;
            detail.HomeRealmUrl = (tbHomeRealmUri.Text.Length > 0 ? tbHomeRealmUri.Text : null);

            try
            {
                FillDetails();

                if (MessageBox.Show(this, "Do you want to connect now to this server?", "Question", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes)
                {
                    doConnect = true;
                }

                DialogResult = true;
                Close();
            }
            catch (Exception error)
            {
                MessageBox.Show(this, error.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        private void FillDetails()
        {
            bool hasFoundOrg = false;

            OrganizationDetail selectedOrganization = comboBoxOrganizations.SelectedItem != null ? ((OrgDetail)comboBoxOrganizations.SelectedItem).Detail : null;

            if (organizations == null || organizations.Count == 0)
            {
                FillOrganizations();
            }

            if (organizations != null && selectedOrganization != null)
                foreach (var organization in organizations)
                {
                    if (organization.UniqueName == selectedOrganization.UniqueName)
                    {
                        detail.OrganizationServiceUrl =
                            organization.Endpoints[EndpointType.OrganizationService];
                        detail.Organization = organization.UniqueName;
                        detail.OrganizationFriendlyName = organization.FriendlyName;

                        detail.ConnectionName = txtConnectionName.Text;

                        if (isCreationMode)
                        {
                            detail.ConnectionId = Guid.NewGuid();
                        }

                        hasFoundOrg = true;

                        break;
                    }
                }

            if (!hasFoundOrg)
            {
                throw new Exception("Unable to match selected organization with list of organizations in this deployment");
            }
        }

        public void FillOrganizations()
        {
            try
            {

                WebRequest.GetSystemWebProxy();
                var connection = CrmConnection.Parse(detail.GetDiscoveryCrmConnectionString());
                var service = new DiscoveryService(connection);

                var request = new RetrieveOrganizationsRequest();
                var response = (RetrieveOrganizationsResponse)service.Execute(request);

                //return response.Details;
                organizations = new List<OrganizationDetail>();

                // Obtain information about the organizations that the system user belongs to.
                var orgsCollection = response.Details;

                foreach (var orgDetail in orgsCollection)
                {
                    organizations.Add(orgDetail);

                    comboBoxOrganizations.Items.Add(new OrgDetail { Detail = orgDetail });

                    //AddItem(comboBoxOrganizations, new Organization() { OrganizationDetail = orgDetail });

                    //SelectUniqueValue(comboBoxOrganizations);
                }

                comboBoxOrganizations.SelectedIndex = 0;

                // SetEnableState(comboBoxOrganizations, true);
            }
            catch (Exception error)
            {
                string errorMessage = CrmExceptionHelper.GetErrorMessage(error, false);

                MessageBox.Show(errorMessage);

                //if (error.InnerException != null)
                //DisplayMessageBox(this, "Error while retrieving organizations : " + errorMessage + "\r\n" + error.InnerException.Message + "\r\n" + error.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                //else
                //DisplayMessageBox(this, "Error while retrieving organizations : " + errorMessage + "\r\n" + error.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            finally
            {
                //SetCursor(this, Cursors.Default);
            }
        }

        private void FillValues()
        {
            rbCustom.IsChecked = detail.IsCustomAuth;

            txtConnectionName.Text = detail.ConnectionName;
            tbServerName.Text = detail.ServerName;
            tbServerPort.Text = detail.ServerPort;
            tbDomain.Text = detail.UserDomain;
            tbLogin.Text = detail.UserName;
            tbPassword.Password = detail.UserPassword;
            cbSavePassword.IsChecked = detail.SavePassword;
            comboBoxOrganizations.Text = detail.OrganizationFriendlyName;
            cbUseIfd.IsChecked = detail.UseIfd;
            cbUserOsdp.IsChecked = detail.UseOsdp;
            cbUseOnline.IsChecked = detail.UseOnline;
            cbUseSsl.IsChecked = detail.UseSsl;
            tbHomeRealmUri.Text = detail.HomeRealmUrl;

            tbHomeRealmUri.IsEnabled = detail.UseIfd;
        }

        #endregion Methods

    }
}
